package tg.game.util.timeline;

import java.text.SimpleDateFormat;
import java.util.Date;


/**
 * 时间点执行任务
 * @author NKW
 * 指定时间点执行的任务
 * 属性：
 *    第一次时间点过了是否需要马上执行一次后，再计算出下次执行时间
 *    
 */
public abstract class TimeTask implements Runnable{
	public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static int STATE_CANCEL = 0;//取消状态
	public static int STATE_WAITE_EXEC = 1;//等待执行状态
	public static int STATE_EXECUTING = 2;//正在执行状态
	public static int STATE_EXEC_COMPLETE = 3;//执行完成状态
	public static int STATE_EXEC_ERROR = 4;//执行完成状态	

	protected long nextExecTime = -1;//每次都要计算出下次执行的时间点，如果是负一就不执行了，结束了，如果不是-1再重新排队，不同周期类型的任务自己去实现
	
	private String name = "time_task";//任务名称
	private int state = STATE_WAITE_EXEC;//默认为等待执行状态
	private boolean isExecNextAfterError = true;//错误后是否继续下次任务,
//	private int excuteCount = 1;//执行次数，默认执行1次,如果是周期性的就是一个周期内执行几次
//	private int excuteType = 0;//类型：0 ：单次连续性的，基本上是一次，也有可能连续执行2次，  1：周期性的
//	private int intervalType = 0;//间隔类型：0固定间隔，间隔1分就是固定每1分执行一次 ；  1：本次任务执行完后再按指定间隔1分再执行
	
	
	public void setNextExecTime(long time){
		this.nextExecTime = time;//第一次执行设定执行时间，以后的执行，是任务完了之后再算出来的
	}
	
	public long getNextExecTime(){
		//计算出下次执行时间
		return nextExecTime;
	}

	@SuppressWarnings("static-access")
	@Override
	public void run() {
		try {
			this.setExecuting();//设定为正在执行状态
			this.execute();
			this.setComplete();;//设定为执行完成状态
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println("执行任务出错！任务名："+name);
			this.setExecuteError();;//执行出错状态
		}
		if (this.isExecuteError() && !isExecNextAfterError){
			return;//如果执行出错了，不需要继续执行任务，就返回
		}
		this.nextExecTime = this.caculateNextExecTime();
		if (this.nextExecTime < 0){
			return;//如果计算下次执行时间小于0，就不再执行，直接返回
		}
		if (this.nextExecTime > System.currentTimeMillis())//判断一下如果是正常的未来时间就再次加入队列，等待执行
			TimeLine.getInstance().addTask(this);//再次加入排队
		else{
			//实际测试中，遇到电脑休眠时，wait超时不起做用，电脑醒来时，才触发，就造成下次任务是过去的时间，这个情况是否要处理？
			//测试时，间隔5秒中，结果10分钟后休眠，40分后恢复，执行到这里就出了问题，服务器上是不大可能出现（特别是一天一次的不会），但是要防止意外
			//但是间隔短的，不知道会不会有这种情况出现，如果有的话，那就只能是另一种，本次任务结束后再间隔指定时间，这种就可以满足，要灵活判断
			System.err.println("计算出来的下次执行时间有误，是过去的时间，是不合理的！");
		}
	}

	public abstract void execute();//执行任务，任务实现在里面写
	public abstract void release();//释放任务，任务的释放动作在里面写
	/**
	 * 计算出下一次任务执行时间，如果没有下一次了，要返回-1，本方法在任务执行完之后执行，判断是否要再下次执行任务
	 * 此方法其他地方不能调用的，因为计算完下次执行时间，再计算就会跳过一次，千万不能乱调用此方法
	 * @return
	 * 也可以按需第一次调用，比如说每个月的1号执行等，可以先算出下次1号的具体时间，在构造里就调用算出第一次执行时间，以后每次执行完再算出下个月的1号
	 * 类似的实现可以自己灵活
	 */
	protected abstract long caculateNextExecTime();//计算下一次的执行时间，如果是负-1，那就不再执行了
	
	public void cancel(){
		this.state = STATE_CANCEL;
		System.err.println("任务被取消！"+this);//已经在执行的任务是无法取消的，只能是排队中的
		this.release();
	}
	public boolean isCancel() {
		return state == STATE_CANCEL?true:false;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	/**
	 * @return the isComplete
	 */
	public boolean isComplete() {
		return state == STATE_EXEC_COMPLETE;
	}
	/**
	 * @param isComplete the isComplete to set
	 */
	public void setComplete() {
		this.state = STATE_EXEC_COMPLETE;
		System.err.println(format.format(new Date()) + "-->任务执行完成！"+this);
		this.release();
	}
	/**
	 * @return the isExecuteError
	 */
	public boolean isExecuteError() {
		return state == STATE_EXEC_ERROR;
	}
	/**
	 * @param isExecuteError the isExecuteError to set
	 */
	public void setExecuteError() {
		this.state = STATE_EXEC_ERROR;
		System.err.println(format.format(new Date()) + "-->任务执行出错！"+this);
	}
	/**
	 * @return the isExecuting
	 */
	public boolean isExecuting() {
		return state == STATE_EXECUTING;
	}
	/**
	 * @param isExecuting the isExecuting to set
	 */
	public void setExecuting() {
		 state = STATE_EXECUTING;
	}
	/**
	 * @return the state
	 */
	public int getState() {
		return state;
	}
	/**
	 * @param state the state to set
	 */
	public void setState(int state) {
		this.state = state;
	}
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer("[");
		sb.append(name);
		sb.append("]");
		return sb.toString();
	}
}
